﻿using System;

namespace Validity
{
  public struct ComparableNullable<T>
       : IComparable
       where T : struct, IComparable
  {
    private readonly T? _value;

    public Boolean HasValue { get { return _value.HasValue; } }
    public T Value { get { return _value.Value; } }

    public ComparableNullable(T value)
    {
      _value = new Nullable<T>(value);
    }

    public ComparableNullable(T? value)
    {
      _value = value;
    }

    public T GetValueOrDefault()
    {
      return _value.GetValueOrDefault();
    }

    public T GetValueOrDefault(T defaultValue)
    {
      return _value.HasValue ? _value.Value : defaultValue;
    }

    public override bool Equals(object other)
    {
      if (other == null)
        return !HasValue;

      if (other is ComparableNullable<T>)
        return CompareTo((ComparableNullable<T>)other) == 0;

      return CompareTo(other as T?) == 0;
    }

    public override int GetHashCode()
    {
      return _value.GetHashCode();
    }

    public override string ToString()
    {
      return _value.ToString();
    }

    public Int32 CompareTo(ComparableNullable<T> other)
    {
      if (!HasValue && !other.HasValue)
        return 0;

      if (HasValue && !other.HasValue)
        return 1;

      if (!HasValue && other.HasValue)
        return -1;

      return Value.CompareTo(other.Value);
    }

    Int32 IComparable.CompareTo(Object other)
    {
      if (other is ComparableNullable<T>)
        return CompareTo((ComparableNullable<T>)other);

      return CompareTo(other as T?);
    }
    
    public static implicit operator ComparableNullable<T>(Nullable<T> value)
    {
      return new ComparableNullable<T>(value);
    }

    public static implicit operator ComparableNullable<T>(T value)
    {

      return new ComparableNullable<T>(new Nullable<T>(value));
    }

    public static explicit operator Nullable<T>(ComparableNullable<T> value)
    {
      return value._value;
    }

    public static explicit operator T(ComparableNullable<T> value)
    {
      return value._value.GetValueOrDefault();
    }
  }
}

